#include "Audio.h"

SFX::SFX(Audio* prnt)
{
	volume = pan = 0;
	volInc = panInc = 100;
	looping = false;
	play = false;
	parent = prnt;
}

SFX::~SFX()
{
	buffer->Release();
	buffer = NULL;
}

void SFX::update()
{
	if (play)
	{
		parent->playSound(buffer);
		play = !play;
	}
	else if (looping)
		parent->playSoundLoop(buffer);
	if(parent->parent->keyboard->isKeyPressed(KEY_X))
	{
		parent->stopSound(buffer);
		looping = false;
		parent->parent->rave = false;
	}
}

void SFX::changeVol(bool volUp)
{
	if (volUp)
		volume+=volInc;
	else
		volume-=volInc;
	if (volume > DSBVOLUME_MAX)
		volume = DSBVOLUME_MAX;
	if (volume < DSBVOLUME_MIN)
		volume = DSBVOLUME_MIN;
	buffer->SetVolume(volume);
}

void SFX::changePan(bool panLeft)
{
	if (panLeft)
		pan+=panInc;
	else
		pan-=panInc;
	if (pan > DSBPAN_RIGHT)
		pan = DSBPAN_RIGHT;
	if (pan < DSBPAN_LEFT)
		pan = DSBPAN_LEFT;

	buffer->SetPan(pan);
}

Audio::Audio(Game *prnt)
{
	parent = prnt;
}

Audio::~Audio()
{
	// shutdown DirectSound
	shutdownDirectSound();
}

bool Audio::Init()
{
	if (!initDirectSound(parent->wndHandle))
	{
		MessageBox(NULL, "Unable to init DirectSound", "ERROR", MB_OK);
		return false;
	}
	rave = new SFX(this);
	beep = new SFX(this);
	kirby = new SFX(this);

	rave->setBuff(LoadWaveToSoundBuffer("sandstorm-short.wav"));
	beep->setBuff(LoadWaveToSoundBuffer("beep.wav"));
	kirby->setBuff(LoadWaveToSoundBuffer("kirby_music.wav"));
	kirby->changeLoop();

	if (!rave->getBuff())
		return false;

	return true;
}

void Audio::update()
{
	if (parent->keyboard->isKeyPressed(KEY_SPACE))
		beep->changePlay();
	if (parent->keyboard->isKeyPressed(KEY_R))
	{
		rave->changeLoop();
		parent->rave = true;
	}
	rave->update();
	beep->update();
	kirby->update();
	if (parent->keyboard->isKeyPressed(KEY_X))
		kirby->changeLoop();
}

/*******************************************************************
* shutdownDirectSound
* Releases the DirecSound device
*******************************************************************/
void Audio::shutdownDirectSound(void)
{
	if (g_pDS)
	{
		g_pDS->Release();
		g_pDS = NULL;
	}
}

/*******************************************************************
* initDirectSound
* Initializes DirectSound
*******************************************************************/
bool Audio::initDirectSound(HWND hwnd)
{
	HRESULT hr;

	hr = DirectSoundCreate8( NULL, &g_pDS, NULL );
	if FAILED (hr)
		return false;

	// Set DirectSound cooperative level 
    hr = g_pDS->SetCooperativeLevel( hwnd, DSSCL_PRIORITY );
	if FAILED ( hr )
		return false;

	return true;
}

/*******************************************************************
* LoadWaveToSoundBuffer
* Loads a wave file into a DirectSound Buffer
*******************************************************************/
LPDIRECTSOUNDBUFFER Audio::LoadWaveToSoundBuffer(std::string wavFilename)
{
	LPDIRECTSOUNDBUFFER apDSBuffer = NULL;
	CWaveFile *wavFile;
	HRESULT hr;

	wavFile = new CWaveFile();
	wavFile->Open((char*)wavFilename.c_str(), NULL, WAVEFILE_READ );
	if( wavFile->GetSize() == 0 )
	{
		MessageBox(parent->wndHandle, "invalid file", "ERROR", MB_OK);
		return false;
	}


	DSBUFFERDESC dsbd;
    ZeroMemory( &dsbd, sizeof(DSBUFFERDESC) );
    dsbd.dwSize          = sizeof(DSBUFFERDESC);
    dsbd.dwFlags         = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN;
    dsbd.dwBufferBytes   = wavFile->GetSize();
    dsbd.guid3DAlgorithm = GUID_NULL;
	dsbd.lpwfxFormat     = wavFile->m_pwfx;

	hr = g_pDS->CreateSoundBuffer( &dsbd, &apDSBuffer, NULL );
	if FAILED (hr)
	{
		MessageBox(NULL, "unable to create sound buffer", "ERROR", MB_OK);
		return NULL;
	}

	VOID*   pDSLockedBuffer      = NULL; // Pointer to locked buffer memory
    DWORD   dwDSLockedBufferSize = 0;    // Size of the locked DirectSound buffer
    DWORD   dwWavDataRead        = 0;    // Amount of data read from the wav file 
	
	hr = apDSBuffer->Lock( 0, wavFile->GetSize(), 
						   &pDSLockedBuffer, &dwDSLockedBufferSize, 
                           NULL, NULL, 0L );
	if FAILED (hr)
        return NULL;

    // Reset the wave file to the beginning 
    wavFile->ResetFile();

	// Read the wave file
    hr = wavFile->Read( (BYTE*) pDSLockedBuffer,
                       dwDSLockedBufferSize, 
                       &dwWavDataRead );
	// Check to make sure that this was successful
	if FAILED (hr)
        return NULL;

	// Check to make sure the wav file is not empty
    if( dwWavDataRead == 0 )
    {
        // Wav is blank, so just fill with silence
        FillMemory( (BYTE*) pDSLockedBuffer, 
                    dwDSLockedBufferSize, 
                    (BYTE)(wavFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
    }
    else if( dwWavDataRead < dwDSLockedBufferSize )
    {
	    // Don't repeat the wav file, just fill in silence 
        FillMemory( (BYTE*) pDSLockedBuffer + dwWavDataRead, 
                   dwDSLockedBufferSize - dwWavDataRead, 
                   (BYTE)(wavFile->m_pwfx->wBitsPerSample == 8 ? 128 : 0 ) );
    }

    // Unlock the buffer, we don't need it anymore.
    apDSBuffer->Unlock( pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0 );

	// Clean up
	delete wavFile;

	return apDSBuffer;
}


/*******************************************************************
* playSound
* plays a sound currently in a buffer only once
*******************************************************************/
void Audio::playSound(LPDIRECTSOUNDBUFFER whichBuffer)
{
	whichBuffer->Play( 0, 0, 0);
}

/*******************************************************************
* playSoundLoop
* plays a sound in a buffer repeatedly
*******************************************************************/
void Audio::playSoundLoop(LPDIRECTSOUNDBUFFER whichBuffer)
{
	whichBuffer->Play( 0, 0, DSBPLAY_LOOPING );
}

/*******************************************************************
* stopSound
* stops the sound in this buffer from playing
*******************************************************************/
void Audio::stopSound(LPDIRECTSOUNDBUFFER whichBuffer)
{
	whichBuffer->Stop();
}